package com.se.net;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.drasyl.channel.tun.InetProtocol;
import org.drasyl.channel.tun.Tun4Packet;

/**
 * @author hdu_huang
 * @date 2024/3/4 004 23:20.
 */
public class ICMP {

    /*
    ICMP报文: Type和Code定义ICMP的报文类型
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |    Type        |  Code        |          Checksum             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         Body[不同类型，携带不同报文]                             |
     */

    public static final int INET4_VERSION_AND_HEADER_LENGTH = 0;

    //TCP flags
    int TCP_URG_MASK = 0x0020;
    int TCP_ACK_MASK = 0x0010;
    int TCP_PSH_MASK = 0x0008;
    int TCP_RST_MASK = 0x0004;
    int TCP_SYN_MASK = 0x0002;
    int TCP_FIN_MASK = 0x0001;


    //全是无符号整数
    static class TCPv4Header {
        short srcPort;    // 源端口
        short destPort;    // 目标端口
        int seq;      // 序列号
        int ack_seq;  // 确认号
        char len;      // 首部长度
        char flag;     // 标志位
        short win;      // 窗口大小
        short checksum; // 校验和
        short urg;      // 紧急指针
    }


    //ICMP type/code不同
    //Echo Request：8  Echo Reply：0
    byte type = 0;
    byte code = 0;
    //2bytes
    char checksum = 0;
    char Identifier;
    char sequenceNumber;
    byte[] data;


    //在TCP/IP中，校验和主要应用于IP、ICMP、UDP和TCP报文头部。 反码加法:不依赖系统是大端小端, 计算和验证简单、高效
    //计算校验和   1.将校验和设为0  2.16位无符号一组进行反码加法计算  3.累加和超过16bit，产生了进位，将高16bit和低16bit累加求和，4.对计算结果取反
    //验证校验和   checksum: 无符号反码加法  ~c=a+b  checksum:无符号反码加法  ~c=~(a+b)
    // sum = (sum >> 16) + (sum & 0xffff);
    // sum += (sum >> 16);
    // return (~sum) & 0xffff;
    //major  checksum:无符号反码加法 T=0xffff, T = nT, T = a + ~a, ~T = 0
    // a = (a1+a2...an), ~a = T - a = nT - (a1+a2...an) = ~a1 + ~a2 + ... + ~an
    public static int calculateChecksum(final ByteBuf buf, int offset, int length) {
        int sum = 0;
        for (int i = offset; i < length; i += 2) {
            sum += buf.getUnsignedShort(i);
        }

        sum = (sum & 0xffff) + (sum >> 16);
        // 高16位和低16位累加求和后可能还有进位，需要再计算一次
        sum += (sum>>16);

        return (~sum) & 0xffff;
    }

    public static int inverseUnsignedShortAdd(int a, int b) {

        //max(a+b) = 2 x 0xffff, 所以最多产生一次进位
        int sum = a + b;

        // 高16位和低16位累加求和后，不会有进位
        sum = (sum & 0xffff) + (sum >> 16);
        return (~sum) & 0xffff;
    }

    //加12字节伪首部，包含源IP地址、目的IP地址、保留字节、传输层协议号和TCP报文长度 4+4+1+1+2
    //UDP协议号(17) TCP协议号(6)
    public static int fakeIpHeaderChecksum(String srcIp, String destIp, int protocol, int length) {

        byte[] bytes = fakeIpHeader(srcIp, destIp, protocol, length);
        ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);
        int result = calculateChecksum(byteBuf, 0, bytes.length);

        return result & 0xffff;
    }

    //加12字节伪首部，包含源IP地址、目的IP地址、保留字节、传输层协议号和TCP报文长度 4+4+1+1+2
    //srcIp, destIp, Zeros, Protocol, TCP-Length[ipLen-ipHeaderLen]
    public static byte[] fakeIpHeader(String srcIp, String destIp, int protocol, int length) {

        byte[] fakeIpHeader = new byte[12];

        String[] src = srcIp.split("\\.");
        String[] dest = destIp.split("\\.");

        for (int i = 0; i < 4; i++) {
            fakeIpHeader[i] = (byte) (Integer.parseInt(src[i]));
        }

        for (int i = 4; i < 8; i++) {
            fakeIpHeader[i] = (byte) (Integer.parseInt(dest[i - 4]));
        }

        fakeIpHeader[8] = 0x00;
        fakeIpHeader[9] = (byte) (protocol & 0xff);

        fakeIpHeader[10] = (byte) (length >> 8 & 0xff);
        fakeIpHeader[11] = (byte) (length & 0xff);

        return fakeIpHeader;
    }


    public static void main(String[] args) throws Exception {

        //getUnsignedShort自动补0, 0x01 => 0x01,0x00 256
        int unsignedShort = Unpooled.buffer().writeBytes(new byte[]{1}).getUnsignedShort(0);


        //ICMP src=47.99.35.166, dst=192.168.254.129   ip checksum:6fc9[28617]  checksum 0x555a[21850]
        String result = "4500003cb8c4000080016fc92f6323a6c0a8fe810000555a000100016162636465666768696a6b6c6d6e6f7071727374757677616263646566676869";
        byte[] bytes1 = getHexBytes(result);

        Tun4Packet packet1 = new Tun4Packet(Unpooled.buffer().writeBytes(bytes1));
        System.out.println(packet1);
        System.out.println(InetProtocol.protocolByDecimal(packet1.protocol()));

        //IGMPv3 src 169.254.105.251  dest 224.0.0.22 ip.len24   check 0x9e9d 40605  [ip_length 24]
        String hex = "46 00 00 28 92 22 00 00 01 02 9e 9d a9 fe 69 fb e0 00 00 16 94 04 00 00 22 00 f9 01 00 00 00 01 04 00 00 00 e0 00 00 fc";

        byte[] bytes2 = getHexBytes(hex);
        ByteBuf byteBuf1 = Unpooled.copiedBuffer(bytes2);

        //byteBuf1.setShort(10, 0);
        int unsignedByte = byteBuf1.getUnsignedShort(10);
        int i = calculateChecksum(byteBuf1, 0, 24);


        int sum = 0;
        sum = 0xffff + 1;
        sum = (sum & 0xffff) + (sum >> 16);
        sum = (~sum) & 0xffff;

        Tun4Packet packet = new Tun4Packet(byteBuf1);



        System.out.println(packet.internetHeaderLength() * 4);
        System.out.println(packet.verifyChecksum()); //false

    }

    public static byte[] getHexBytes(String hexString) {

        hexString = hexString.replaceAll(" ", "");
        byte[] bytes = new byte[hexString.length() / 2];

        for (int i = 0; i < hexString.length() / 2; i++) {
            bytes[i] = (byte) Integer.parseInt(hexString.substring(i * 2, i * 2 + 2), 16);
        }

        return bytes;
    }

}
